/**
 *
 */
package com.angel.io.type.rows.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.collections15.CollectionUtils;
import org.apache.commons.collections15.Transformer;
import org.apache.log4j.Logger;

import com.angel.common.helpers.CollectionHelper;
import com.angel.common.helpers.StringHelper;
import com.angel.io.exceptions.AliasNameRepeteadException;
import com.angel.io.exceptions.ColumnRowHeaderException;
import com.angel.io.exceptions.HeaderColumnFileNotFoundException;
import com.angel.io.exceptions.HeaderColumnNameRepeatedException;
import com.angel.io.separator.ColumnSeparator;
import com.angel.io.type.cols.HeaderColumnFile;

/**
 * @author William
 *
 */
public class HeaderRowFile{

	private static final String DUMMY_COLUMN_NAME = "Dummy_";
	private static final Logger LOGGER = Logger.getLogger(HeaderRowFile.class);

	private List<HeaderColumnFile> columns;

	public HeaderRowFile(){
		super();
		this.columns = new ArrayList<HeaderColumnFile>();
	}

	public void addColumn(Integer position, String columnName){
		if(!StringHelper.isEmpty(columnName) && !this.containsHeaderColumn(columnName)){
			LOGGER.info("Adding in the header, column [" + columnName + "] at position [" + position + "].");
			HeaderColumnFile column = new HeaderColumnFile(columnName);
			column.setPosition(position);
			for(int i = this.columns.size(); i < position; i++){
				this.columns.add(this.createDummyHeaderColumnFile(i));
			}
			this.columns.add(column);
		} else {
			if(!StringHelper.isEmpty(columnName)){
				String columnNames = this.getAllColumnNamesConcated();
				LOGGER.error("Column [" + columnName + "] is repeted at header [" + columnNames + "].");
				throw new HeaderColumnNameRepeatedException("Column [" + columnName + "] is repeted at header [" + columnNames + "].");
			}
		}
	}

	public void replaceColumn(Integer position, String columnName){
		if(!StringHelper.isEmpty(columnName) && !this.containsHeaderColumn(columnName)
				&& position >= 0 && position < this.columns.size()){
			LOGGER.info("Replacing in the header, column [" + columnName + "] at position [" + position + "].");
			HeaderColumnFile column = new HeaderColumnFile(columnName);
			column.setPosition(position);
			this.columns.add(position, column);
		} else {
			if(!StringHelper.isEmpty(columnName)){
				String columnNames = this.getAllColumnNamesConcated();
				LOGGER.error("Column [" + columnName + "] is repeted at header [" + columnNames + "].");
				throw new HeaderColumnNameRepeatedException("Column [" + columnName + "] is repeted at header [" + columnNames + "].");
			}
		}
	}

	public void addFirstColumn(String columnName){
		Integer firstPosition = Integer.valueOf(0);
		if(!this.hasColumnAt(firstPosition)){
			this.addColumn(firstPosition, columnName);
		} else {
			throw new ColumnRowHeaderException("Column position [0] is a invalid position at the header [" + this.columns.size() + " columns added].");
		}
	}

	public boolean hasColumnAt(Integer position){
		boolean hasColumn = false;
		for(HeaderColumnFile column: this.columns){
			if( !hasColumn && column.getPosition().intValue() == position.intValue()){
				hasColumn = true;
			}
		}
		return hasColumn;
	}

	/** At a column at the next position of the last column added. If there is not columns added, it adds at
	 * the first position.
	 *
	 * @param columnName to add.
	 */
	public void addNextColumn(String columnName){
		Integer nextPosition = this.findMaxPosition();
		if(nextPosition.intValue() == -1){
			this.addColumn(Integer.valueOf(0), columnName);
		} else {
			this.addColumn(nextPosition + 1, columnName);
		}
	}

	public void checkPositionColumn(Integer position) {
		LOGGER.info("Checking in the header, column position to add.");
		if(position <= this.columns.size()){
			throw new ColumnRowHeaderException("Column position [" + position.toString() + "] is a invalid position at the header [" + this.columns.size() + " columns added].");
		}
	}

	private HeaderColumnFile createDummyHeaderColumnFile(int position) {
		LOGGER.info("Creating a dummy column [" + DUMMY_COLUMN_NAME + String.valueOf(position) + "] at position [" + position + "].");
		HeaderColumnFile column = new HeaderColumnFile(DUMMY_COLUMN_NAME + String.valueOf(position));
		column.setPosition(position);
		return column;
	}

	public boolean containsHeaderColumn(String columnName) {
		boolean contains = false;
		for(HeaderColumnFile col: this.columns){
			if(col.isNamed(columnName)){
				contains = true;
				return contains;
			}
		}
		return contains;
	}

	public int getColumnPosition(String columnName){
		return this.findPositionFor(columnName);
	}

	public String getColumnName(int position){
		for(HeaderColumnFile col: this.columns){
			if(col.getPosition().intValue() == position){
				return col.getColumnName();
			}
		}
		return "";
	}

	private Integer findPositionFor(String columnName){
		if(this.containsHeaderColumn(columnName)){
			for(HeaderColumnFile col: this.columns){
				if(col.isNamed(columnName)){
					return col.getPosition();
				}
			}
		}
		return -1;
	}

	public List<String> getAllNamesFor(String columnName){
		List<String> names = new ArrayList<String>();
		Integer position = this.findPositionFor(columnName);
		if(position != -1){
			names.add(columnName);
			HeaderColumnFile col = this.columns.get(position);
			names.addAll(col.getAlias());
		}
		return names;
	}

	public void addAliasFor(String columnName, String alias){
		Integer position = this.getColumnPosition(columnName);
		if(position != -1){
			/** Add an alias "alias" to columnName. */
			HeaderColumnFile col = this.columns.get(position);
			if(!this.containsHeaderRowFileAlias(alias)){
				col.addAliasName(alias);
			} else {
				throw new AliasNameRepeteadException("Alias name [" + alias + "] for column [" + columnName + "] + exist like alias at column [" + col.getColumnName() + "].");
			}
		} else {
			/** Add an alias "columnName" to alias. */
			Integer aliasPosition = this.getColumnPosition(alias);
			if(aliasPosition != -1){
				this.addColumnNameAsAlias(columnName, aliasPosition);
			} else {
				String columnNames = this.getAllColumnNamesConcated();
				throw new HeaderColumnFileNotFoundException("Column [" + columnName + "] not found at header row file with columns [" + columnNames + "].");
			}
		}
	}

	private void addColumnNameAsAlias(String columnName, int aliasPosition) {
		HeaderColumnFile aliasColumn = this.columns.get(aliasPosition);
		if(!this.containsHeaderRowFileAlias(columnName)){
			aliasColumn.addAliasName(columnName);
		} else {
			throw new AliasNameRepeteadException("Alias name [" + columnName + "] for column [" + aliasColumn.getColumnName() + "] + exist like alias at column [" + aliasColumn.getColumnName() + "].");
		}
	}

	private String getAllColumnNamesConcated(){
		return StringHelper.convertToPlainString((List<?>)this.columns);
	}

	public void addAllAliasFor(String columnName, Collection<String> aliasNames){
		for(String alias: aliasNames){
			this.addAliasFor(columnName, alias);
		}
	}

	private boolean containsHeaderRowFileAlias(String alias) {
		for(HeaderColumnFile col: this.columns){
			if(col.containsAlias(alias)){
				return true;
			}
		}
		return false;
	}

	public boolean hasAllColumnName(String... columnNames){
		boolean contains = true;
		for(String columnName: columnNames){
			if(contains && !this.containsHeaderColumn(columnName)){
				contains = false;
			}
		}
		return contains;
	}

	public void removeColumns() {
		this.columns.clear();
	}

	@Override
	public String toString(){
		return this.getAllColumnNamesConcated();
	}

	public Integer quantityColumns(){
		return this.columns.size();
	}

	public Integer findMaxPosition() {
		Integer max = -1;
		for(HeaderColumnFile column: this.columns){
			if(max < column.getPosition()){
				max = column.getPosition();
			}
		}
		return max;
	}

	public String prepareHeader(ColumnSeparator columnSeparator) {
		List<String> columnNames = new ArrayList<String>();
		for(HeaderColumnFile column : this.columns){
			if(this.columns.get(this.columns.size() -1).equals(column)){
				columnNames.add(column.getColumnName());
			} else {
				columnNames.add(column.getColumnName() + columnSeparator.getValue());
			}
		}
		return StringHelper.convertToPlainString(columnNames);
	}
	
	public String[] getColumnsNotContained(String[] columnsName){
		List<String> columns = new ArrayList<String>();
		if(columnsName != null && columnsName.length > 0){
			for(String columnName: columnsName){
				if(!this.containsHeaderColumn(columnName)){
					columns.add(columnName);
				}
			}
		}
		return CollectionHelper.convertGenericTo(columns);
	}
	
	public Collection<HeaderColumnFile> getUniqueHeaderColumns(){
		List<HeaderColumnFile> columns = new ArrayList<HeaderColumnFile>();
		for(HeaderColumnFile columnHeader: this.columns){
			if(columnHeader.isUnique()){
				columns.add(columnHeader);
			}
		}
		return columns;
	}
	
	public boolean isUniqueColumn(String columnName){
		int position = this.getColumnPosition(columnName);
		HeaderColumnFile column = null;
		if(position > -1){
			column = this.columns.get(position);
		}
		return column != null ? column.isUnique(): false;
	}
	
	public Collection<String> getUniqueColumnsNames(){
		Collection<HeaderColumnFile> columns = this.getUniqueHeaderColumns();
		Collection<String> headerUniqueNames = 
			CollectionUtils.transformedCollection(columns, new Transformer<HeaderColumnFile, String>(){
				public String transform(HeaderColumnFile headerColumnFile) {
					return headerColumnFile.getColumnName();
				}
		});
		return headerUniqueNames;
	}
}
